package cn.dakaqi.component;

import org.hibernate.SQLQuery;
import org.hibernate.transform.ResultTransformer;
import org.hibernate.transform.Transformers;
import org.springframework.context.annotation.Scope;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.PageRequest;
import org.springframework.stereotype.Component;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;
import java.math.BigInteger;
import java.util.List;

@Component
@Scope("prototype")
public class EntityManagerUtil {

    @PersistenceContext
    private EntityManager em;

    public synchronized List createNativeQueryList(String sql, Class cls) {
        try {
            if (!em.isOpen()) {
                em = em.getEntityManagerFactory().createEntityManager();
            }
            Query q = em.createNativeQuery(sql, cls);
            return q.getResultList();
        } finally {
            destroy();
        }
    }

    public synchronized List createNativeQueryList(String sql, ResultTransformer transformer) {
        try {
            if (!em.isOpen()) {
                em = em.getEntityManagerFactory().createEntityManager();
            }
            SQLQuery q = em.createNativeQuery(sql).unwrap(SQLQuery.class);
            q.setResultTransformer(transformer);
            return q.list();
        } finally {
            destroy();
        }
    }

    public synchronized Page createNativeQueryPage(String query, int pageIndex, int pageSize,
                                                   ResultTransformer transformer) {
        try {
            if (!em.isOpen()) {
                em = em.getEntityManagerFactory().createEntityManager();
            }
            String countSql = "select count(1) from (" + query + ") as a";
            SQLQuery q = em.createNativeQuery(countSql).unwrap(SQLQuery.class);
            Long total = ((BigInteger) q.uniqueResult()).longValue();
            StringBuilder buffer = new StringBuilder(query);
            if (pageIndex == 0 || pageSize == 0) {
                pageIndex = 0;
                pageSize = Integer.MAX_VALUE;
            } else {
                buffer.append(" limit ").append(pageSize).append(" offset ").append((pageIndex - 1) * pageSize);
            }
            q = em.createNativeQuery(buffer.toString()).unwrap(SQLQuery.class);
            q.setResultTransformer(transformer);
            return new PageImpl<>(q.list(), new PageRequest(pageIndex, pageSize), total);
        } finally {
            destroy();
        }
    }

    public synchronized List createNativeQueryList(String sql, ResultTransformer transformer, Object[] params) {
        try {
            if (!em.isOpen()) {
                em = em.getEntityManagerFactory().createEntityManager();
            }
            Query q = em.createNativeQuery(sql);
            for (int i = 0; i < params.length; i++) {
                q = q.setParameter(1 + i, params[i]);
            }
            SQLQuery query = q.unwrap(SQLQuery.class);
            query.setResultTransformer(transformer);
            return query.list();
        } finally {
            destroy();
        }
    }

    public synchronized Object createNativeQuerySingle(String sql) {
        try {
            if (!em.isOpen()) {
                em = em.getEntityManagerFactory().createEntityManager();
            }
            SQLQuery q = em.createNativeQuery(sql).unwrap(SQLQuery.class);
            // q.setResultTransformer(transformer);
            return q.uniqueResult();
        } finally {
            destroy();
        }
    }

    public synchronized Object createNativeQuerySingle(String sql, Class cls) {
        try {
            if (!em.isOpen()) {
                em = em.getEntityManagerFactory().createEntityManager();
            }
            SQLQuery q = em.createNativeQuery(sql).unwrap(SQLQuery.class);
            q.setResultTransformer(Transformers.aliasToBean(cls));
            return q.uniqueResult();
        } finally {
            destroy();
        }
    }

    public synchronized Object createNativeQuerySingle(String sql, ResultTransformer transformer, Object[] params) {
        try {
            if (!em.isOpen()) {
                em = em.getEntityManagerFactory().createEntityManager();
            }
            Query q = em.createNativeQuery(sql);
            for (int i = 0; i < params.length; i++) {
                q = q.setParameter(1 + i, params[i]);
            }
            SQLQuery query = q.unwrap(SQLQuery.class);
            query.setResultTransformer(transformer);
            return query.uniqueResult();
        } finally {
            destroy();
        }
    }

    public synchronized void execute(String sql) {
        try {
            if (!em.isOpen()) {
                em = em.getEntityManagerFactory().createEntityManager();
            }
            em.createNativeQuery(sql).executeUpdate();
        } finally {
            destroy();
        }
    }

    public synchronized void batchReplace(String sql, List<Object[]> list) {
        try {
            if (!em.isOpen()) {
                em = em.getEntityManagerFactory().createEntityManager();
            }
            int row = 0;
            for (Object[] objects : list) {
                Query q = em.createNativeQuery(sql);
                for (int i = 0; i < objects.length; i++) {
                    q = q.setParameter(1 + i, objects[i]);
                }
                q.executeUpdate();
                row++;
                if (row % 100 == 0) {
                    em.flush();
                }
            }

        } finally {
            destroy();
        }
    }

    private void destroy() {
        // em.flush();
        if (em.isOpen()) {
            em.clear();
            em.close();
        }
        // em = null;
    }

}